#include "../copyright"

#include "seta.h"
#include "memmap.h"

/* Mode 7 scaling constants for all raster lines */
const int16_t ST010_M7Scale[176] =
{
   0x0380,  0x0325,  0x02da,  0x029c,  0x0268,  0x023b,  0x0215,  0x01f3,
   0x01d5,  0x01bb,  0x01a3,  0x018e,  0x017b,  0x016a,  0x015a,  0x014b,
   0x013e,  0x0132,  0x0126,  0x011c,  0x0112,  0x0109,  0x0100,  0x00f8,
   0x00f0,  0x00e9,  0x00e3,  0x00dc,  0x00d6,  0x00d1,  0x00cb,  0x00c6,
   0x00c1,  0x00bd,  0x00b8,  0x00b4,  0x00b0,  0x00ac,  0x00a8,  0x00a5,
   0x00a2,  0x009e,  0x009b,  0x0098,  0x0095,  0x0093,  0x0090,  0x008d,
   0x008b,  0x0088,  0x0086,  0x0084,  0x0082,  0x0080,  0x007e,  0x007c,
   0x007a,  0x0078,  0x0076,  0x0074,  0x0073,  0x0071,  0x006f,  0x006e,
   0x006c,  0x006b,  0x0069,  0x0068,  0x0067,  0x0065,  0x0064,  0x0063,
   0x0062,  0x0060,  0x005f,  0x005e,  0x005d,  0x005c,  0x005b,  0x005a,
   0x0059,  0x0058,  0x0057,  0x0056,  0x0055,  0x0054,  0x0053,  0x0052,
   0x0051,  0x0051,  0x0050,  0x004f,  0x004e,  0x004d,  0x004d,  0x004c,
   0x004b,  0x004b,  0x004a,  0x0049,  0x0048,  0x0048,  0x0047,  0x0047,
   0x0046,  0x0045,  0x0045,  0x0044,  0x0044,  0x0043,  0x0042,  0x0042,
   0x0041,  0x0041,  0x0040,  0x0040,  0x003f,  0x003f,  0x003e,  0x003e,
   0x003d,  0x003d,  0x003c,  0x003c,  0x003b,  0x003b,  0x003a,  0x003a,
   0x003a,  0x0039,  0x0039,  0x0038,  0x0038,  0x0038,  0x0037,  0x0037,
   0x0036,  0x0036,  0x0036,  0x0035,  0x0035,  0x0035,  0x0034,  0x0034,
   0x0034,  0x0033,  0x0033,  0x0033,  0x0032,  0x0032,  0x0032,  0x0031,
   0x0031,  0x0031,  0x0030,  0x0030,  0x0030,  0x0030,  0x002f,  0x002f,
   0x002f,  0x002e,  0x002e,  0x002e,  0x002e,  0x002d,  0x002d,  0x002d,
   0x002d,  0x002c,  0x002c,  0x002c,  0x002c,  0x002b,  0x002b,  0x002b
};

ST010_Regs ST010;

uint8_t S9xGetST010(uint32_t Address)
{
   if (!(Address & 0x80000))
      return 0x80;
   if ((Address & 0xFFF) == 0x20)
      return ST010.op_reg;
   if ((Address & 0xFFF) == 0x21)
      return ST010.execute;
   return Memory.SRAM[Address & Memory.SRAMMask];
}

const int16_t ST010_SinTable[256] =
{
    0x0000,  0x0324,  0x0648,  0x096a,  0x0c8c,  0x0fab,  0x12c8,  0x15e2,
    0x18f9,  0x1c0b,  0x1f1a,  0x2223,  0x2528,  0x2826,  0x2b1f,  0x2e11,
    0x30fb,  0x33df,  0x36ba,  0x398c,  0x3c56,  0x3f17,  0x41ce,  0x447a,
    0x471c,  0x49b4,  0x4c3f,  0x4ebf,  0x5133,  0x539b,  0x55f5,  0x5842,
    0x5a82,  0x5cb3,  0x5ed7,  0x60eb,  0x62f1,  0x64e8,  0x66cf,  0x68a6,
    0x6a6d,  0x6c23,  0x6dc9,  0x6f5e,  0x70e2,  0x7254,  0x73b5,  0x7504,
    0x7641,  0x776b,  0x7884,  0x7989,  0x7a7c,  0x7b5c,  0x7c29,  0x7ce3,
    0x7d89,  0x7e1d,  0x7e9c,  0x7f09,  0x7f61,  0x7fa6,  0x7fd8,  0x7ff5,
    0x7fff,  0x7ff5,  0x7fd8,  0x7fa6,  0x7f61,  0x7f09,  0x7e9c,  0x7e1d,
    0x7d89,  0x7ce3,  0x7c29,  0x7b5c,  0x7a7c,  0x7989,  0x7884,  0x776b,
    0x7641,  0x7504,  0x73b5,  0x7254,  0x70e2,  0x6f5e,  0x6dc9,  0x6c23,
    0x6a6d,  0x68a6,  0x66cf,  0x64e8,  0x62f1,  0x60eb,  0x5ed7,  0x5cb3,
    0x5a82,  0x5842,  0x55f5,  0x539b,  0x5133,  0x4ebf,  0x4c3f,  0x49b4,
    0x471c,  0x447a,  0x41ce,  0x3f17,  0x3c56,  0x398c,  0x36ba,  0x33df,
    0x30fb,  0x2e11,  0x2b1f,  0x2826,  0x2528,  0x2223,  0x1f1a,  0x1c0b,
    0x18f8,  0x15e2,  0x12c8,  0x0fab,  0x0c8c,  0x096a,  0x0648,  0x0324,
    0x0000, -0x0324, -0x0648, -0x096b, -0x0c8c, -0x0fab, -0x12c8, -0x15e2,
   -0x18f9, -0x1c0b, -0x1f1a, -0x2223, -0x2528, -0x2826, -0x2b1f, -0x2e11,
   -0x30fb, -0x33df, -0x36ba, -0x398d, -0x3c56, -0x3f17, -0x41ce, -0x447a,
   -0x471c, -0x49b4, -0x4c3f, -0x4ebf, -0x5133, -0x539b, -0x55f5, -0x5842,
   -0x5a82, -0x5cb3, -0x5ed7, -0x60ec, -0x62f1, -0x64e8, -0x66cf, -0x68a6,
   -0x6a6d, -0x6c23, -0x6dc9, -0x6f5e, -0x70e2, -0x7254, -0x73b5, -0x7504,
   -0x7641, -0x776b, -0x7884, -0x7989, -0x7a7c, -0x7b5c, -0x7c29, -0x7ce3,
   -0x7d89, -0x7e1d, -0x7e9c, -0x7f09, -0x7f61, -0x7fa6, -0x7fd8, -0x7ff5,
   -0x7fff, -0x7ff5, -0x7fd8, -0x7fa6, -0x7f61, -0x7f09, -0x7e9c, -0x7e1d,
   -0x7d89, -0x7ce3, -0x7c29, -0x7b5c, -0x7a7c, -0x7989, -0x7883, -0x776b,
   -0x7641, -0x7504, -0x73b5, -0x7254, -0x70e2, -0x6f5e, -0x6dc9, -0x6c23,
   -0x6a6d, -0x68a6, -0x66cf, -0x64e8, -0x62f1, -0x60eb, -0x5ed7, -0x5cb3,
   -0x5a82, -0x5842, -0x55f5, -0x539a, -0x5133, -0x4ebf, -0x4c3f, -0x49b3,
   -0x471c, -0x447a, -0x41cd, -0x3f17, -0x3c56, -0x398c, -0x36b9, -0x33de,
   -0x30fb, -0x2e10, -0x2b1f, -0x2826, -0x2527, -0x2223, -0x1f19, -0x1c0b,
   -0x18f8, -0x15e2, -0x12c8, -0x0fab, -0x0c8b, -0x096a, -0x0647, -0x0324
};

const uint8_t ST010_ArcTan[32][32] =
{
   {
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
   },
   {
      0x80, 0xa0, 0xad, 0xb3, 0xb6, 0xb8, 0xb9, 0xba, 0xbb, 0xbb, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
      0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf
   },
   {
      0x80, 0x93, 0xa0, 0xa8, 0xad, 0xb0, 0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb,
      0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd
   },
   {
      0x80, 0x8d, 0x98, 0xa0, 0xa6, 0xaa, 0xad, 0xb0, 0xb1, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb7, 0xb8,
      0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbc
   },
   {
      0x80, 0x8a, 0x93, 0x9a, 0xa0, 0xa5, 0xa8, 0xab, 0xad, 0xaf, 0xb0, 0xb2, 0xb3, 0xb4, 0xb5, 0xb5,
      0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb
   },
   {
      0x80, 0x88, 0x90, 0x96, 0x9b, 0xa0, 0xa4, 0xa7, 0xa9, 0xab, 0xad, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
      0xb4, 0xb4, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9
   },
   {
      0x80, 0x87, 0x8d, 0x93, 0x98, 0x9c, 0xa0, 0xa3, 0xa6, 0xa8, 0xaa, 0xac, 0xad, 0xae, 0xb0, 0xb0,
      0xb1, 0xb2, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8
   },
   {
      0x80, 0x86, 0x8b, 0x90, 0x95, 0x99, 0x9d, 0xa0, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac, 0xad, 0xae,
      0xaf, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7
   },
   {
      0x80, 0x85, 0x8a, 0x8f, 0x93, 0x97, 0x9a, 0x9d, 0xa0, 0xa2, 0xa5, 0xa6, 0xa8, 0xaa, 0xab, 0xac,
      0xad, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5
   },
   {
      0x80, 0x85, 0x89, 0x8d, 0x91, 0x95, 0x98, 0x9b, 0x9e, 0xa0, 0xa0, 0xa4, 0xa6, 0xa7, 0xa9, 0xaa,
      0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4
   },
   {
      0x80, 0x84, 0x88, 0x8c, 0x90, 0x93, 0x96, 0x99, 0x9b, 0x9e, 0xa0, 0xa2, 0xa4, 0xa5, 0xa7, 0xa8,
      0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3
   },
   {
      0x80, 0x84, 0x87, 0x8b, 0x8e, 0x91, 0x94, 0x97, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa3, 0xa5, 0xa6,
      0xa7, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2
   },
   {
      0x80, 0x83, 0x87, 0x8a, 0x8d, 0x90, 0x93, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa3, 0xa5,
      0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1
   },
   {
      0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x94, 0x96, 0x99, 0x9b, 0x9d, 0x9e, 0xa0, 0xa2, 0xa3,
      0xa4, 0xa5, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xb0
   },
   {
      0x80, 0x83, 0x86, 0x89, 0x8b, 0x8e, 0x90, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9d, 0x9e, 0xa0, 0xa1,
      0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf
   },
   {
      0x80, 0x83, 0x85, 0x88, 0x8b, 0x8d, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9b, 0x9d, 0x9f, 0xa0,
      0xa1, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae
   },
   {
      0x80, 0x83, 0x85, 0x88, 0x8a, 0x8c, 0x8f, 0x91, 0x93, 0x95, 0x97, 0x99, 0x9a, 0x9c, 0x9d, 0x9f,
      0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xab, 0xac, 0xad
   },
   {
      0x80, 0x82, 0x85, 0x87, 0x89, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x97, 0x99, 0x9b, 0x9c, 0x9d,
      0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac
   },
   {
      0x80, 0x82, 0x85, 0x87, 0x89, 0x8b, 0x8d, 0x8f, 0x91, 0x93, 0x95, 0x96, 0x98, 0x99, 0x9b, 0x9c,
      0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab
   },
   {
      0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x95, 0x97, 0x98, 0x9a, 0x9b,
      0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa
   },
   {
      0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x99, 0x9a,
      0x9b, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9
   },
   {
      0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8f, 0x90, 0x92, 0x94, 0x95, 0x97, 0x98, 0x99,
      0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8
   },
   {
      0x80, 0x82, 0x84, 0x86, 0x87, 0x89, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x98,
      0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7
   },
   {
      0x80, 0x82, 0x84, 0x85, 0x87, 0x89, 0x8a, 0x8c, 0x8e, 0x8f, 0x91, 0x92, 0x94, 0x95, 0x96, 0x98,
      0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5, 0xa6
   },
   {
      0x80, 0x82, 0x83, 0x85, 0x87, 0x88, 0x8a, 0x8c, 0x8d, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x96, 0x97,
      0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5
   },
   {
      0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x94, 0x95, 0x96,
      0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4
   },
   {
      0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x89, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x95,
      0x96, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4
   },
   {
      0x80, 0x82, 0x83, 0x85, 0x86, 0x87, 0x89, 0x8a, 0x8c, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x93, 0x95,
      0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2, 0xa3
   },
   {
      0x80, 0x81, 0x83, 0x84, 0x86, 0x87, 0x89, 0x8a, 0x8b, 0x8d, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94,
      0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2
   },
   {
      0x80, 0x81, 0x83, 0x84, 0x86, 0x87, 0x88, 0x8a, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93,
      0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa1
   },
   {
      0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8b, 0x8c, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x93,
      0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1
   },
   {
      0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92,
      0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0
   }
};

int16_t ST010_Sin(int16_t Theta)
{
   return ST010_SinTable[(Theta >> 8) & 0xff];
}

int16_t ST010_Cos(int16_t Theta)
{
   return ST010_SinTable[((Theta + 0x4000) >> 8) & 0xff];
}

void ST010_OP01(int16_t x0, int16_t y0, int16_t* x1, int16_t* y1, int16_t* Quadrant, int16_t* Theta)
{
   if ((x0 < 0) && (y0 < 0))
   {
      *x1 = -x0;
      *y1 = -y0;
      *Quadrant = -0x8000;
   }
   else if (x0 < 0)
   {
      *x1 = y0;
      *y1 = -x0;
      *Quadrant = -0x4000;
   }
   else if (y0 < 0)
   {
      *x1 = -y0;
      *y1 = x0;
      *Quadrant = 0x4000;
   }
   else
   {
      *x1 = x0;
      *y1 = y0;
      *Quadrant = 0x0000;
   }

   while ((*x1 > 0x1f) || (*y1 > 0x1f))
   {
      if (*x1 > 1)
         *x1 >>= 1;
      if (*y1 > 1)
         *y1 >>= 1;
   }

   if(*y1 == 0)
      *Quadrant += 0x4000;

   *Theta = (ST010_ArcTan[*y1][*x1] << 8) ^ *Quadrant;
}

void ST010_Scale(int16_t Multiplier, int16_t X0, int16_t Y0, int32_t* X1, int32_t* Y1)
{
   *X1 = X0 * Multiplier << 1;
   *Y1 = Y0 * Multiplier << 1;
}

void ST010_Multiply(int16_t Multiplicand, int16_t Multiplier, int32_t* Product)
{
   *Product = Multiplicand * Multiplier << 1;
}

void ST010_Rotate(int16_t Theta, int16_t X0, int16_t Y0, int16_t* X1, int16_t* Y1)
{
   *X1 = (Y0 * ST010_Sin(Theta) >> 15) + (X0 * ST010_Cos(Theta) >> 15);
   *Y1 = (Y0 * ST010_Cos(Theta) >> 15) - (X0 * ST010_Sin(Theta) >> 15);
}

void ST010_SortDrivers(uint16_t Positions, uint16_t Places[32], uint16_t Drivers[32])
{
   bool Sorted;
   uint16_t Temp;

   if(Positions > 1)
   {
      do
      {
         int32_t i;

         Sorted = true;
         for(i = 0; i < Positions - 1; i++)
         {
            if(Places[i] < Places[i + 1])
            {
               Temp = Places[i + 1];
               Places[i + 1] = Places[i];
               Places[i] = Temp;

               Temp = Drivers[i + 1];
               Drivers[i + 1] = Drivers[i];
               Drivers[i] = Temp;

               Sorted = false;
            }
         }
         Positions--;
      } while(!Sorted);
   }
}

#define ST010_WORD(offset) (Memory.SRAM[offset + 1] << 8) | Memory.SRAM[offset]

void S9xSetST010(uint32_t Address, uint8_t Byte)
{
   if (!(Address & 0x80000))
   {
      ST010.control_enable = true;
      return;
   }

   if ((Address & 0xFFF) == 0x20 && ST010.control_enable)
      ST010.op_reg = Byte;
   if ((Address & 0xFFF) == 0x21 && ST010.control_enable)
      ST010.execute = Byte;
   else
      Memory.SRAM[Address & Memory.SRAMMask] = Byte;

   if (ST010.execute & 0x80)
   {
      switch (ST010.op_reg)
      {
      /* Sorts Driver Placements
         Input
           0x0024-0x0025 : Positions
           0x0040-0x007f : Places
           0x0080-0x00ff : Drivers
         Output
           0x0040-0x007f : Places
           0x0080-0x00ff : Drivers */
      case 0x02:
      {
#if defined(FAST_LSB_WORD_ACCESS) && !defined(ANDROID)
         /* TODO - FIXME */
         ST010_SortDrivers(*(int16_t*)&Memory.SRAM[0x0024], (uint16_t*)(Memory.SRAM + 0x0040), (uint16_t*)(Memory.SRAM + 0x0080));
#else
         uint16_t Places[32];
         uint16_t Positions = ST010_WORD(0x0024);
         int32_t Pos, Offset;

         Offset = 0;

         for (Pos = 0; Pos < Positions; Pos++)
         {
            Places[Pos] = ST010_WORD(0x0040 + Offset);
            Offset += 2;
         }

         ST010_SortDrivers(Positions, Places, (uint16_t*)(Memory.SRAM + 0x0080));

         Offset = 0;

         for (Pos = 0; Pos < Positions; Pos++)
         {
            Memory.SRAM[0x0040 + Offset] = (uint8_t)(Places[Pos]);
            Memory.SRAM[0x0041 + Offset] = (uint8_t)(Places[Pos] >> 8);
            Offset += 2;
         }
#endif
         break;

      }

      /* Two Dimensional Coordinate Scale
         Input
           0x0000-0x0001 : X0 (signed)
           0x0002-0x0003 : Y0 (signed)
           0x0004-0x0005 : Multiplier (signed)
         Output
           0x0010-0x0013 : X1 (signed)
           0x0014-0x0017 : Y1 (signed) */
      case 0x03:
      {
#if defined(FAST_LSB_WORD_ACCESS) && !defined(ANDROID)
         /* TODO - FIXME */
         ST010_Scale(*(int16_t*)&Memory.SRAM[0x0004], *(int16_t*)&Memory.SRAM[0x0000],
                     *(int16_t*)&Memory.SRAM[0x0002], (int32_t*)&Memory.SRAM[0x0010], (int32_t*)&Memory.SRAM[0x0014]);
#else
         int32_t x1, y1;

         ST010_Scale(ST010_WORD(0x0004), ST010_WORD(0x0000), ST010_WORD(0x0002), &x1, &y1);

         Memory.SRAM[0x0010] = (uint8_t)(x1);
         Memory.SRAM[0x0011] = (uint8_t)(x1 >> 8);
         Memory.SRAM[0x0012] = (uint8_t)(x1 >> 16);
         Memory.SRAM[0x0013] = (uint8_t)(x1 >> 24);
         Memory.SRAM[0x0014] = (uint8_t)(y1);
         Memory.SRAM[0x0015] = (uint8_t)(y1 >> 8);
         Memory.SRAM[0x0016] = (uint8_t)(y1 >> 16);
         Memory.SRAM[0x0017] = (uint8_t)(y1 >> 24);
#endif
         break;
      }

      /* 16-bit Multiplication
         Input
           0x0000-0x0001 : Multiplcand (signed)
           0x0002-0x0003 : Multiplier (signed)
         Output
           0x0010-0x0013 : Product (signed) */
      case 0x06:
      {
#if defined(FAST_LSB_WORD_ACCESS) && !defined(ANDROID)
         /* TODO - FIXME */
         ST010_Multiply(*(int16_t*)&Memory.SRAM[0x0000], *(int16_t*)&Memory.SRAM[0x0002], (int32_t*)&Memory.SRAM[0x0010]);
#else
         int32_t Product;

         ST010_Multiply(ST010_WORD(0x0000), ST010_WORD(0x0002), &Product);

         Memory.SRAM[0x0010] = (uint8_t)(Product);
         Memory.SRAM[0x0011] = (uint8_t)(Product >> 8);
         Memory.SRAM[0x0012] = (uint8_t)(Product >> 16);
         Memory.SRAM[0x0013] = (uint8_t)(Product >> 24);
#endif
         break;
      }

      /* Mode 7 Raster Data Calculation
         Input
           0x0000-0x0001 : Angle (signed)
         Output
           0x00f0-0x024f : Mode 7 Matrix A
           0x0250-0x03af : Mode 7 Matrix B
           0x03b0-0x050f : Mode 7 Matrix C
           0x0510-0x066f : Mode 7 Matrix D */
      case 0x07:
      {
         int16_t data;
         int32_t offset = 0;
         int16_t Theta = ST010_WORD(0x0000);

         int32_t line;
         for (line = 0; line < 176; line++)
         {
            /* Calculate Mode 7 Matrix A/D data */
            data = ST010_M7Scale[line] * ST010_Cos(Theta) >> 15;

            Memory.SRAM[0x00f0 + offset] = (uint8_t)(data);
            Memory.SRAM[0x00f1 + offset] = (uint8_t)(data >> 8);
            Memory.SRAM[0x0510 + offset] = (uint8_t)(data);
            Memory.SRAM[0x0511 + offset] = (uint8_t)(data >> 8);

            /* Calculate Mode 7 Matrix B/C data */
            data = ST010_M7Scale[line] * ST010_Sin(Theta) >> 15;

            Memory.SRAM[0x0250 + offset] = (uint8_t)(data);
            Memory.SRAM[0x0251 + offset] = (uint8_t)(data >> 8);

            if(data)
               data = ~data;

            Memory.SRAM[0x03b0 + offset] = (uint8_t)(data);
            Memory.SRAM[0x03b1 + offset] = (uint8_t)(data >> 8);

            offset += 2;
         }

         /* Shift Angle for use with Lookup table */
         Memory.SRAM[0x00] = Memory.SRAM[0x01];
         Memory.SRAM[0x01] = 0x00;

         break;
      }

      /* Two dimensional Coordinate Rotation
         Input
           0x0000-0x0001 : X0 (signed)
           0x0002-0x0003 : Y0 (signed)
           0x0004-0x0005 : Angle (signed)
         Output
           0x0010-0x0011 : X1 (signed)
           0x0012-0x0013 : Y1 (signed) */
      case 0x08:
      {
#if defined(FAST_LSB_WORD_ACCESS) && !defined(ANDROID)
         /* TODO - FIXME */
         ST010_Rotate(*(int16_t*)&Memory.SRAM[0x0004], *(int16_t*)&Memory.SRAM[0x0000],
                      *(int16_t*)&Memory.SRAM[0x0002], (int16_t*)&Memory.SRAM[0x0010], (int16_t*)&Memory.SRAM[0x0012]);
#else
         int16_t x1, y1;

         ST010_Rotate(ST010_WORD(0x0004), ST010_WORD(0x0000), ST010_WORD(0x0002), &x1, &y1);

         Memory.SRAM[0x0010] = (uint8_t)(x1);
         Memory.SRAM[0x0011] = (uint8_t)(x1 >> 8);
         Memory.SRAM[0x0012] = (uint8_t)(y1);
         Memory.SRAM[0x0013] = (uint8_t)(y1 >> 8);
#endif
         break;
      }

      /* Input
           0x0000-0x0001 : DX (signed)
           0x0002-0x0003 : DY (signed)
         Output
           0x0010-0x0011 : Angle (signed) */
      case 0x01:
      {
         Memory.SRAM[0x0006] = Memory.SRAM[0x0002];
         Memory.SRAM[0x0007] = Memory.SRAM[0x0003];

#if defined(FAST_LSB_WORD_ACCESS) && !defined(ANDROID)
         /* TODO - FIXME */
         ST010_OP01(*(int16_t*)&Memory.SRAM[0x0000], *(int16_t*)&Memory.SRAM[0x0002],
                    (int16_t*) &Memory.SRAM[0x0000], (int16_t*) &Memory.SRAM[0x0002],
                    (int16_t*) &Memory.SRAM[0x0004], (int16_t*) &Memory.SRAM[0x0010]);
#else
         {
         int16_t x1, y1, Quadrant, Theta;

         ST010_OP01(ST010_WORD(0x0000), ST010_WORD(0x0002), &x1, &y1, &Quadrant, &Theta);

         Memory.SRAM[0x0000] = (uint8_t)(x1);
         Memory.SRAM[0x0001] = (uint8_t)(x1 >> 8);
         Memory.SRAM[0x0002] = (uint8_t)(y1);
         Memory.SRAM[0x0003] = (uint8_t)(y1 >> 8);
         Memory.SRAM[0x0004] = (uint8_t)(Quadrant);
         Memory.SRAM[0x0005] = (uint8_t)(Quadrant >> 8);
         Memory.SRAM[0x0010] = (uint8_t)(Theta);
         Memory.SRAM[0x0011] = (uint8_t)(Theta >> 8);
         }
#endif
         break;
      }

      /* calculate the vector length of (x,y) */
      case 0x04:
      {
         int16_t square, x, y;
#if defined(FAST_LSB_WORD_ACCESS) && !defined(ANDROID)
         /* TODO - FIXME */
         x = *((int16_t*)Memory.SRAM);
         y = *((int16_t*)&Memory.SRAM[2]);
#else
         x = Memory.SRAM[0] | (Memory.SRAM[1] << 8);
         y = Memory.SRAM[2] | (Memory.SRAM[3] << 8);
#endif
         square = (int16_t)_isqrt((int32_t) x * x + (int32_t) y * y);

#if defined(FAST_LSB_WORD_ACCESS) && !defined(ANDROID)
         /* TODO - FIXME */
         *((int16_t*)&Memory.SRAM[0x10]) = square;
#else
         Memory.SRAM[0x10] = (uint8_t)(square);
         Memory.SRAM[0x11] = (uint8_t)(square >> 8);
#endif
         break;
      }

      /* calculate AI orientation based on specific guidelines */
      case 0x05:
      {
         int32_t dx, dy;
         int16_t a1, b1, c1;
         uint16_t o1;
         uint16_t old_speed;

         bool wrap = false;

         /* target (x,y) coordinates */
         int16_t ypos_max = ST010_WORD(0x00C0);
         int16_t xpos_max = ST010_WORD(0x00C2);

         /* current coordinates and direction */
         int32_t ypos = Memory.SRAM[0xC4] | (Memory.SRAM[0xC5] << 8) | (Memory.SRAM[0xC6] << 16) | (Memory.SRAM[0xC7] << 24);
         int32_t xpos = Memory.SRAM[0xC8] | (Memory.SRAM[0xC9] << 8) | (Memory.SRAM[0xCA] << 16) | (Memory.SRAM[0xCB] << 24);
         uint16_t rot = Memory.SRAM[0xCC] | (Memory.SRAM[0xCD] << 8);

         /* physics */
         uint16_t speed = ST010_WORD(0x00D4);
         uint16_t accel = ST010_WORD(0x00D6);
         uint16_t speed_max = ST010_WORD(0x00D8);

         /* special condition acknowledgment */
         int16_t system = ST010_WORD(0x00DA);
         int16_t flags = ST010_WORD(0x00DC);

         /* new target coordinates */
         int16_t ypos_new = ST010_WORD(0x00DE);
         int16_t xpos_new = ST010_WORD(0x00E0);

         /* mask upper bit */
         xpos_new &= 0x7FFF;

         /* get the current distance */
         dx = xpos_max - (xpos >> 16);
         dy = ypos_max - (ypos >> 16);

         /* quirk: clear and move in9 */
         Memory.SRAM[0xD2] = 0xFF;
         Memory.SRAM[0xD3] = 0xFF;
         Memory.SRAM[0xDA] = 0;
         Memory.SRAM[0xDB] = 0;

         /* grab the target angle */
         ST010_OP01(dy, dx, &a1, &b1, &c1, (int16_t*)&o1);

         /* check for wrapping */
         if (ABS(o1 - rot) > 0x8000)
         {
            o1 += 0x8000;
            rot += 0x8000;
            wrap = true;
         }

         old_speed = speed;

         if (ABS(o1 - rot) == 0x8000) /* special case */
            speed = 0x100;
         else if (ABS(o1 - rot) >= 0x1000) /* slow down for sharp curves */
         {
            uint32_t slow = ABS(o1 - rot);
            slow >>= 4; /* scaling */
            speed -= slow;
         }
         else /* otherwise accelerate */
         {
            speed += accel;
            if (speed > speed_max)
               speed = speed_max; /* clip speed */
         }

         /* prevent negative/positive overflow */
         if (ABS(old_speed - speed) > 0x8000)
         {
            if(old_speed < speed)
               speed = 0;
            else
               speed = 0xff00;
         }

         /* adjust direction by so many degrees; be careful of negative adjustments */
         if((o1 > rot && (o1 - rot) > 0x80) || (o1 < rot && (rot - o1) >= 0x80))
         {
            if(o1 < rot)
               rot -= 0x280;
            else if(o1 > rot)
               rot += 0x280;
         }

         /* turn off wrapping */
         if(wrap)
            rot -= 0x8000;

         /* now check the distances (store for later) */
         dx = (xpos_max << 16) - xpos;
         dy = (ypos_max << 16) - ypos;
         dx >>= 16;
         dy >>= 16;

         /* if we're in so many units of the target, signal it */
         if ((system && (dy <= 6 && dy >= -8) && (dx <= 126 && dx >= -128)) || (!system && (dx <= 6 && dx >= -8) && (dy <= 126 && dy >= -128)))
         {
            /* announce our new destination and flag it */
            xpos_max = xpos_new & 0x7FFF;
            ypos_max = ypos_new;
            flags |= 0x08;
         }

         /* update position */
         xpos -= (ST010_Cos(rot) * 0x400 >> 15) * (speed >> 8) << 1;
         ypos -= (ST010_Sin(rot) * 0x400 >> 15) * (speed >> 8) << 1;

         /* quirk: mask upper byte */
         xpos &= 0x1FFFFFFF;
         ypos &= 0x1FFFFFFF;

         Memory.SRAM[0x00C0] = (uint8_t)(ypos_max);
         Memory.SRAM[0x00C1] = (uint8_t)(ypos_max >> 8);
         Memory.SRAM[0x00C2] = (uint8_t)(xpos_max);
         Memory.SRAM[0x00C3] = (uint8_t)(xpos_max >> 8);
         Memory.SRAM[0x00C4] = (uint8_t)(ypos);
         Memory.SRAM[0x00C5] = (uint8_t)(ypos >> 8);
         Memory.SRAM[0x00C6] = (uint8_t)(ypos >> 16);
         Memory.SRAM[0x00C7] = (uint8_t)(ypos >> 24);
         Memory.SRAM[0x00C8] = (uint8_t)(xpos);
         Memory.SRAM[0x00C9] = (uint8_t)(xpos >> 8);
         Memory.SRAM[0x00CA] = (uint8_t)(xpos >> 16);
         Memory.SRAM[0x00CB] = (uint8_t)(xpos >> 24);
         Memory.SRAM[0x00CC] = (uint8_t)(rot);
         Memory.SRAM[0x00CD] = (uint8_t)(rot >> 8);
         Memory.SRAM[0x00D4] = (uint8_t)(speed);
         Memory.SRAM[0x00D5] = (uint8_t)(speed >> 8);
         Memory.SRAM[0x00DC] = (uint8_t)(flags);
         Memory.SRAM[0x00DD] = (uint8_t)(flags >> 8);
         break;
      }
      }

      /* lower signal: op processed */
      ST010.op_reg = 0;
      ST010.execute = 0;
   }
}
